package org.snake.spring.framework.beans.support;

import org.snake.spring.framework.beans.config.RAYBeanDefinition;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * Bean读取器
 * <p>
 * 主要的职责:
 * 1.解析配置文件
 * 2.读取配置文件
 */
public class RAYBeanDefinitionReader
{

    /**
     * 加载配置文件
     */
    private Properties contextConfig = new Properties();

    /**
     * 缓存需要注册的所有类的全类名
     */
    private List<String> registryBeanClasses = new ArrayList<>();

    /**
     * 构造器
     *
     * @param configLocations 本地配置文件
     */
    public RAYBeanDefinitionReader(String... configLocations)
    {

        // 1.加载配置文件
        doLoadConfig(configLocations[0]);
        // 2.扫描配置文件中配置位置的所有类
        doScanner(contextConfig.getProperty("scanPackage"));

    }

    /**
     * 配置文件中加载出来的beanDefinition集合
     *
     * @return List<RAYBeanDefinition>
     */
    public List<RAYBeanDefinition> doLoadRAYBeanDefinitions()
    {

        List<RAYBeanDefinition> resultList = new ArrayList<>();

        try
        {
            for (String className : registryBeanClasses)
            {
                Class<?> beanClass = Class.forName(className);

                if (beanClass.isInterface())
                {
                    continue;
                }

                resultList.add(doCreateBeanDefinition(toLowerFirstName(beanClass.getSimpleName()), beanClass.getName()));

                // 处理实现接口的类，这些类可以通过接口去创建实例。所以将接口的类型全名作为key，value为同一个类实例
                for (Class<?> i : beanClass.getInterfaces())
                {
                    resultList.add(doCreateBeanDefinition(i.getName(), beanClass.getName()));
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        return resultList;

    }

    /**
     * 创建RAYBeanDefinition
     *
     * @param factoryBeanName 类名称
     * @param beanClassName   全类名
     * @return RAYBeanDefinition
     */
    private RAYBeanDefinition doCreateBeanDefinition(String factoryBeanName, String beanClassName)
    {
        // 创建对象
        RAYBeanDefinition beanDefinition = new RAYBeanDefinition();
        // 设置类名，首字母小写
        beanDefinition.setFactoryBeanName(factoryBeanName);
        // 设置全类名
        beanDefinition.setBeanClassName(beanClassName);
        // 返回对象
        return beanDefinition;
    }

    /**
     * 类名的首字母小写
     *
     * @param simpleName 类名称
     * @return String 首字母小写后的类名称
     */
    private String toLowerFirstName(String simpleName)
    {

        // 转换为ASCII码
        char[] chars = simpleName.toCharArray();
        // 同个字母的小写字母比大写字母要大32
        chars[0] += 32;
        return String.valueOf(chars);
    }

    /**
     * 加载配置文件
     */
    private void doLoadConfig(String contextConfigLocation)
    {

        // 从类路径下找到配置文件
        try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation.replaceAll("classpath:", "")))
        {
            contextConfig.load(is);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 扫描相关的类
     *
     * @param scanPackage 扫描包路径
     */
    private void doScanner(String scanPackage)
    {

        // 1.获取包下的所有class文件
        // 包路径org.snake.demo中的"."替换成"/".即org/snake/demo的文件路径
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));

        // 2.获取路径下的所有文件
        if (null != url)
        {
            // 类路径下文件
            File classPath = new File(url.getFile());

            // 循环获取类路径下的所有文件
            for (File file : classPath.listFiles())
            {

                if (null == file)
                {
                    continue;
                }
                // 迭代扫描子文件夹
                if (file.isDirectory())
                {
                    // 递归的一个扫描
                    doScanner(scanPackage + "." + file.getName());
                }
                else
                {
                    // 过滤掉非class的文件
                    if (!file.getName().endsWith(".class"))
                    {
                        continue;
                    }
                    // 2.1 拿到文件名
                    String fileName = file.getName();
                    // 2.2 通过 包名 + 文件名，再截取掉文件名的后缀，即可得到全类名
                    String className = (scanPackage + "." + fileName.replaceAll(".class", ""));
                    // 2.3 注册这个类的全类名，通过全类名，才能够通过反射Class.forName()去实例化
                    registryBeanClasses.add(className);
                }
            }
        }
    }

    /**
     * 获取配置文件信息
     *
     * @return Properties
     */
    public Properties getConfiguration()
    {

        return this.contextConfig;
    }

}
